#include <pd_gpio.h>
#include <hd_oled_protocol_i2c.h>
#include <hd_led.h>
#include <pd_timer_ch.h>
#include <pd_timer.h>
#include <pd_timer_oc.h>
#include <pd_timer_pwm.h>
#include <hd_oled.h>
#include <delay.h>
#include <pd_timer_ic.h>
#include <pd_i2c_hd.h>
#include <hd_flash.h>
#include "main.h"


#define OLED_PROTOCOL       2   // Select a protocol. SPI, 1; I2C, 2;
#define OLED_PROTOCOL_IMPL  2   // Select a implement. simulate, 1; hardware, 2;

static OLED *oled;

void oled_instance();

void timer_pwm_init(uint32_t freq, uint8_t duty);

extern void SystemClock_Config(void);


int main(void) {
    SystemClock_Config();

    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

    oled_instance();
    oled->showString(oled, 0, 0, "TIM IC");
    oled->showString(oled, 0, 16, "Freq:00000000Hz");
    oled->showString(oled, 0, 32, "duty:00%");
    oled->refresh(oled);

    // Set Pin PC6-AF2-TIM3_CH1 as IC channel, TIM3_CH2 as indirect channel
    GPIO *gpio = GPIOBuilder.build(GPIOC, LL_GPIO_PIN_6);
    gpio->Mode = LL_GPIO_MODE_ALTERNATE;
    gpio->Alternate = LL_GPIO_AF_2;
    uint32_t channel[] = {LL_TIM_CHANNEL_CH1, LL_TIM_CHANNEL_CH2};
    TimerCh *timerChannel = TimerChBuilder.build(&gpio, 1, channel, 2);
    Timer *timer = TimerBuilder.build(TIM3, 84 - 1, 65536 - 1);
    TimerIC *timerIC = TimerICBuilder.build(timer, timerChannel);
    timerIC->init(timerIC, 1);

    timer_pwm_init(12000, 30);

    while (1) {
        oled->showNum(oled, 40, 16, timerIC->getFreq(timerIC), 8);
        oled->showNum(oled, 40, 32, timerIC->getDuty(timerIC), 2);
        oled->refresh(oled);
        delay_ms(100);
    }
}


void timer_pwm_init(uint32_t freq, uint8_t duty) {
    // GPIO and Channel: PB6 -- TIM4_CH1; PB7 -- TIM4_CH2; PB8 -- TIM4_CH3; PB9 -- TIM4_CH4
    GPIO *gpio[] = {GPIOBuilder.build(GPIOB, LL_GPIO_PIN_6 | LL_GPIO_PIN_7 | LL_GPIO_PIN_8 | LL_GPIO_PIN_9)};
    gpio[0]->Mode = LL_GPIO_MODE_ALTERNATE;
    gpio[0]->Alternate = LL_GPIO_AF_2;
    uint32_t channel[] = {LL_TIM_CHANNEL_CH1, LL_TIM_CHANNEL_CH2, LL_TIM_CHANNEL_CH3, LL_TIM_CHANNEL_CH4};
    TimerCh *timerChannel = TimerChBuilder.build(gpio, 1, channel, 4);

    // resolution = 1%, freq = CK_PSC / (PSC + 1) / (ARR + 1)
    Timer *timer = TimerBuilder.build(TIM4, 84000000 / 100 / freq - 1, 100 - 1);
    // CCR = Duty * (ARR + 1)
    TimerOC *timerOC = TimerOCBuilder.build(timer, timerChannel, duty * 100);
    TimerPWM *timerPWM = TimerPWMBuilder.build(timer, timerOC);
    timerPWM->init(timerPWM);
    timerPWM->setPWMDuty(timerPWM, LL_TIM_CHANNEL_CH1, duty + 10);
    timerPWM->setPWMDuty(timerPWM, LL_TIM_CHANNEL_CH2, duty + 20);
    timerPWM->setPWMDuty(timerPWM, LL_TIM_CHANNEL_CH3, duty + 30);
    timerPWM->setPWMDuty(timerPWM, LL_TIM_CHANNEL_CH4, duty + 40);
}

void oled_instance() {
#if OLED_PROTOCOL == 1
    GPIO *sckGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_5); // Clock, SPIx_SCK
    GPIO *sdaGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_7); // MOSI,  SPIx_MOSI
    GPIO *resGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_3); // Reset, General IO, the chip reset pin is better
    GPIO *dcGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_1);  // Data or Command, General IO
    GPIO *csGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_2);  // Chip Select(Slave Select), General IO
    SPIGPIO *spiGPIO = SPIGPIOBuilder.build(&csGPIO, 1, sckGPIO, sdaGPIO, NULL);

#if OLED_PROTOCOL_IMPL == 1
    SPISimulate *spiObj = SPISimulateBuilder.build(spiGPIO);
#elif OLED_PROTOCOL_IMPL == 2
    SPIHardware *spiObj = SPIHardwareBuilder.build(SPI1, spiGPIO);
#endif
    SPI *spi1 = (SPI *) spiObj;
    spi1->init(spi1);
    OLEDProtocolSPI *oledProtocolObj = OLEDProtocolSPIBuilder.build(spi1, resGPIO, dcGPIO, csGPIO);

#elif OLED_PROTOCOL == 2
    GPIO *sckGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_10);  // I2Cx_SCL
    GPIO *sdaGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_11);  // I2Cx_SDA
#if OLED_PROTOCOL_IMPL == 1
    I2CSimulate *i2CObj = I2CSimulateBuilder.build(sckGPIO, sdaGPIO);
#elif OLED_PROTOCOL_IMPL == 2
    I2CHardware *i2CObj = I2CHardwareBuilder.build(I2C2, sckGPIO, sdaGPIO);
#endif
    I2C *i2c = (I2C *) i2CObj;
    i2c->init(i2c);
    OLEDProtocolI2C *oledProtocolObj = OLEDProtocolI2CBuilder.build(i2c);
#endif

    StorageFlash *storageFlash = StorageFlashBuilder.build();
    OLEDFont *oledFont = OLEDFontBuilder.build((Storage *) storageFlash, ENCODING_UTF8);
    oled = OLEDBuilder.build((OLEDProtocol *) oledProtocolObj, oledFont);
    oled->init(oled);
}
